package com.bizdk.generator.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import com.bizdk.generator.common.exception.ServerException;
import com.bizdk.generator.common.utils.DateUtils;
import com.bizdk.generator.core.DataModel;
import com.bizdk.generator.core.DataModelConstant;
import com.bizdk.generator.core.template.TemplateLoderUtils;
import com.bizdk.generator.dto.gen.GeneratorInfo;
import com.bizdk.generator.core.template.TemplateConfig;
import com.bizdk.generator.core.template.TemplateInfo;
import com.bizdk.generator.entity.gen.BaseClass;
import com.bizdk.generator.entity.gen.Project;
import com.bizdk.generator.entity.gen.Table;
import com.bizdk.generator.entity.gen.TableColumn;
import com.bizdk.generator.service.BaseClassService;
import com.bizdk.generator.service.DatasrcService;
import com.bizdk.generator.service.GeneratorService;
import com.bizdk.generator.service.ProjectService;
import com.bizdk.generator.service.TableColumnService;
import com.bizdk.generator.service.TableService;
import com.bizdk.generator.service.TypeMappingService;
import com.bizdk.generator.core.TemplateUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;


/**
 * 代码生成
 */
@Service
@Slf4j
@AllArgsConstructor
public class GeneratorServiceImpl implements GeneratorService {
    
    private final DatasrcService datasrcService;
    
    private final TypeMappingService typeMappingService;
    
    private final BaseClassService baseClassService;
    
    private final TemplateLoderUtils templateUtil;
    
    private final TableService tableService;
    
    private final TableColumnService tableColumnService;
    
    private final ProjectService projectService;
    
    @Override
    public void downloadCode(GeneratorInfo generatorInfo, ZipOutputStream zip) {
        for (Long tableId : generatorInfo.getTableIds()) {
            // 代码生成信息
            TemplateConfig templateConfig = templateUtil.getTemplateConfig(generatorInfo.getTemplatePath());
            // 数据模型
            DataModel dataModel = getDataModel(generatorInfo, tableId);
            
            // 渲染模板并输出
            log.info("生成zip文件 目录如下：");
            for (TemplateInfo template : templateConfig.getTemplates()) {
                dataModel.put(DataModelConstant.TEMPLATE_NAME, template.getTemplateName());
                String content = TemplateUtils.getContent(template.getTemplateContent(), dataModel);
                String path = TemplateUtils.getContent(template.getGeneratorPath(), dataModel);
                log.info(path);
                try {
                    // 添加到zip
                    zip.putNextEntry(new ZipEntry(path));
                    IoUtil.writeUtf8(zip, false, content);
                    zip.flush();
                    zip.closeEntry();
                } catch (IOException e) {
                    throw new ServerException("模板写入失败：" + path, e);
                }
            }
        }
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void generatorCode(GeneratorInfo generatorInfo) {
        for (Long tableId : generatorInfo.getTableIds()) {
            generatorInfo.setTableId(tableId);
            Map<String, String> pathCodeMap = previewCode(generatorInfo);
            Iterator<Map.Entry<String, String>> iter = pathCodeMap.entrySet().iterator();
            for (; iter.hasNext(); ) {
                Map.Entry<String, String> next = iter.next();
                log.info("生成文件 {}", next.getKey());
                try {
                    FileUtil.writeUtf8String(next.getValue(), next.getKey());
                } catch (Exception ex) {
                    log.error("保存文件失败, " + next.getKey(), ex);
                }
            }
        }
    }
    
    private void initProjectInfo(GeneratorInfo generatorInfo) {
        Project project = projectService.getById(generatorInfo.getProjectId());
        generatorInfo.setGenType(project.getGenType());
        generatorInfo.setAuthor(project.getAuthor());
        generatorInfo.setEmail(project.getEmail());
        generatorInfo.setPkgName(project.getPkgName());
        generatorInfo.setModuleName(project.getModelName());
        generatorInfo.setCodePath(project.getCodePath());
        generatorInfo.setTemplatePath(project.getTemplatePath());
        generatorInfo.setVersion(project.getVersion());
        
        if (StrUtil.isBlank(generatorInfo.getCodePath())) {
            String wdir = System.getProperty("java.io.tmpdir");
            generatorInfo.setCodePath(wdir + "demo");
        }
    }
    
    @Override
    public Map<String, String> previewCode(GeneratorInfo generatorInfo) {
        Map<String, String> dataMap = new LinkedHashMap<>();
        initProjectInfo(generatorInfo);
        // 数据模型
        DataModel dataModel = getDataModel(generatorInfo, generatorInfo.getTableId());
        // 代码生成信息
        TemplateConfig templateConfig = templateUtil.getTemplateConfig(generatorInfo.getTemplatePath());
        
        // 渲染模板并输出
        for (TemplateInfo template : templateConfig.getTemplates()) {
            dataModel.put(DataModelConstant.TEMPLATE_NAME, template.getTemplateName());
            String content = TemplateUtils.getContent(template.getTemplateContent(), dataModel);
            String path = TemplateUtils.getContent(template.getGeneratorPath(), dataModel);
            dataMap.put(path, content);
        }
        return dataMap;
    }
    
    private DataModel getDataModel(GeneratorInfo generatorInfo, Long tableId) {
        // 数据模型
        DataModel dataModel = getDataModel(tableId);
        
        dataModel.put(DataModelConstant.AUTHOR, generatorInfo.getAuthor());
        dataModel.put(DataModelConstant.EMAIL, generatorInfo.getEmail());
        dataModel.put(DataModelConstant.VERSION, generatorInfo.getVersion());
        // 生成路径
        dataModel.put(DataModelConstant.CODE_PATH, generatorInfo.getCodePath());
        
        // 开发者信息
        setAuthorInfo(dataModel, generatorInfo);
        
        // 项目信息
        setProjectInfo(dataModel, generatorInfo);
        return dataModel;
    }
    
    /**
     * 获取渲染的数据模型
     *
     * @param tableId 表ID
     */
    private DataModel getDataModel(Long tableId) {
        // 表信息
        Table table = tableService.getById(tableId);
        List<TableColumn> columns = tableColumnService.getByTableId(tableId);
        table.setColumns(columns);
        
        // 数据模型
        DataModel dataModel = new DataModel();
        
        // 获取数据库类型
        setDbType(dataModel, table);
        
        // 设置基类信息
        setBaseClass(dataModel, table);
        
        // 导入的包列表
        setImportList(dataModel, table);
        
        // 表信息
        setTableInfo(dataModel, table);
        
        // 设置字段信息
        setFieldTypeList(dataModel, table);
        
        return dataModel;
    }
    
    private void setTableInfo(DataModel dataModel, Table table) {
        dataModel.put(DataModelConstant.TABLE_NAME, table.getTableName());
        dataModel.put(DataModelConstant.TABLE_COMMENT, table.getTableComment());
        dataModel.put(DataModelConstant.BEAN_NAME, table.getBeanName());
        dataModel.put(DataModelConstant.BEAN_NAME_LF, StrUtil.lowerFirst(table.getBeanName()));
        dataModel.put(DataModelConstant.COLUMN_LIST, table.getColumns());
        
        dataModel.put(DataModelConstant.FORM_LAYOUT, table.getFormLayout());
    }
    
    private void setImportList(DataModel dataModel, Table table) {
        Set<String> importList = typeMappingService.getPackageByTableId(table.getId());
        dataModel.put(DataModelConstant.IMPORT_LIST, importList);
    }
    
    private void setDbType(DataModel dataModel, Table table) {
        String dbType = datasrcService.getDatabaseProductName(table.getDatasrcId());
        dataModel.put(DataModelConstant.DB_TYPE, dbType);
    }
    
    private void setProjectInfo(DataModel dataModel, GeneratorInfo generatorInfo) {
        String pkgPath = generatorInfo.getPkgName().replaceAll("\\.", "/");
        dataModel.put(DataModelConstant.PACKAGE, generatorInfo.getPkgName());
        dataModel.put(DataModelConstant.PACKAGE_PATH, pkgPath);
        dataModel.put(DataModelConstant.VERSION, generatorInfo.getVersion());
        dataModel.put(DataModelConstant.MODULE_NAME, generatorInfo.getModuleName());
        dataModel.put(DataModelConstant.MODULE_NAME_UF, StrUtil.upperFirst(generatorInfo.getModuleName()));
    }
    
    private void setAuthorInfo(DataModel dataModel, GeneratorInfo generatorInfo) {
        dataModel.put(DataModelConstant.AUTHOR, generatorInfo.getAuthor());
        dataModel.put(DataModelConstant.EMAIL, generatorInfo.getEmail());
        dataModel.put(DataModelConstant.DATETIME, DateUtils.format(new Date(), DateUtils.DATE_TIME_PATTERN));
        dataModel.put(DataModelConstant.DATE, DateUtils.format(new Date(), DateUtils.DATE_PATTERN));
    }
    
    /**
     * 设置基类信息
     *
     * @param dataModel 数据模型
     * @param table     表
     */
    private void setBaseClass(DataModel dataModel, Table table) {
        if (table.getBaseclassId() == null) {
            return;
        }
        
        // 基类
        BaseClass baseClass = baseClassService.getById(table.getBaseclassId());
        baseClass.setPackageName(baseClass.getPackageName());
        dataModel.put(DataModelConstant.BASE_CLASS, baseClass);
        
        // 基类字段
        String[] attrs = baseClass.getAttrs().split(",");
        // 标注为基类字段
        for (TableColumn columnEntity : table.getColumns()) {
            boolean matchAttr = Arrays.stream(attrs).anyMatch(s -> s.equalsIgnoreCase(columnEntity.getAttrName()));
            boolean matchColumn = Arrays.stream(attrs).anyMatch(s -> s.equalsIgnoreCase(columnEntity.getColumnName()));
            if (matchAttr || matchColumn) {
                columnEntity.setBaseAttr(true);
            }
        }
    }
    
    /**
     * 设置字段分类信息
     *
     * @param dataModel 数据模型
     * @param table     表
     */
    private void setFieldTypeList(DataModel dataModel, Table table) {
        List<TableColumn> columns = tableColumnService.getByTableId(table.getId());
        table.setColumns(columns);
        
        // 主键列表 (支持多主键)
        List<TableColumn> primaryList = new ArrayList<>();
        // 表单列表
        List<TableColumn> formList = new ArrayList<>();
        // 网格列表
        List<TableColumn> gridList = new ArrayList<>();
        // 查询列表
        List<TableColumn> queryList = new ArrayList<>();
        
        for (TableColumn column : table.getColumns()) {
            if (column.isPkItem()) {
                primaryList.add(column);
            }
            if (column.isAddItem() || column.isEditItem()) {
                formList.add(column);
            }
            if (column.isGridItem()) {
                gridList.add(column);
            }
            if (column.isQueryItem()) {
                queryList.add(column);
            }
        }
        dataModel.put(DataModelConstant.PRIMARY_LIST, primaryList);
        dataModel.put(DataModelConstant.FORM_LIST, formList);
        dataModel.put(DataModelConstant.GRID_LIST, gridList);
        dataModel.put(DataModelConstant.QUERY_LIST, queryList);
    }
    
}